/// <reference path="../../Resources/js/jquery/jquery.debug.js" />
/// <reference path="../Items/MItem.js" />
/// <reference name="MicrosoftAjax.debug.js" />
Type.registerNamespace("BM");

// ServiceConstraint object

BM.ServiceConstraint = function(leafItem, additionNode) {
    BM.ServiceConstraint.initializeBase(this, [leafItem, additionNode]);
    this.webRequest = null;
    this._cache = new Object();
}

BM.ServiceConstraint.prototype = {

    getServicePath: function() {
        ///	<summary>        
        ///		returns the service web address
        ///	</summary>
        ///	<returns type="String" />             
        return this.additionNode.getAttribute('servicePath');
    },

    getServiceMethod: function() {
        ///	<summary>        
        ///		returns the service's method name
        ///	</summary>
        ///	<returns type="String" />     
        return this.additionNode.getAttribute('serviceMethod');
    },

    getParameters: function() {
        ///	<summary>        
        ///		returns the parameter's Array
        ///	</summary>
        ///	<returns type="Array" elementType="BM.ServiceParameter" />     
        if (this.parameters == null) {
            this.parameters = BM.ServiceParameter.getParameters(this);
        }
        return this.parameters;
    },

    isMet: function() {
        ///	<summary>
        ///	the method invokes an asynchronous call to the server to check method's result.
        /// Because it cannot wait for the response (no such funcitonality in ASP.NET AJAX),
        /// if the check was not yet performed it returns false 
        /// otherwise, it returns the cached value
        ///	</summary>
        var hashkey = BM.ServiceParameter.getHashKey(this.getParameters());
        var result = this.getFromCache(hashkey);
        if (result == null) {
            this.invokeMethod(hashkey);
            return false;
        }

        var response = Sys.Serialization.JavaScriptSerializer.deserialize('(' + result + ')');
        if (response.ErrorText) {
            this.setErrorText(response.ErrorText);
        }
        return response.IsValid;
    },

    getDefaultErrorText: function() {
        /// <returns type="String">
        /// gets the error text that should be displayed when there is no attached error text
        /// </returns>
        return BM.Messages['ServiceConstraintError'] + this.getServiceMethod();        
    },

    getFromCache: function(hashkey) {
        ///	<summary>        
        ///	gets the cached service response 
        ///	</summary>
        /// <param name="hashkey" type="String" mayBeNull="false">
        /// the hash key, consisting of all parameter values
        /// </param>
        ///	<returns type="Boolean" />     
        return this._cache[hashkey];
    },

    writeToCache: function(hashkey, result) {
        ///	<summary>        
        ///	writes the service result to cache
        ///	</summary>
        /// <param name="hashkey" type="String" mayBeNull="false">
        /// the hash key, consisting of all parameter values
        /// </param>
        /// <param name="result" type="Boolean" mayBeNull="false">
        /// the webservice result
        /// </param>
        this._cache[hashkey] = result;
    },

    onMethodComplete: function(result, context) {
        ///	<summary>
        ///	the method writes the result to cache
        /// in case of a positive result, it performs the validation once again, so that the 'isMet' method 
        /// could return the true information about the validation
        ///	</summary>
        /// <param name="result" type="String" mayBeNull="false">
        /// the service's result
        /// </param>
        /// <param name="context" type="String" mayBeNull="false">
        /// the hash key
        /// </param>
        this.writeToCache(context, result);
        this.webRequest = null;
        if (result) {
            this.item.validate();
        }
    },

    onMethodFailed: function(err, response, context) {
        this.webRequest = null;
    },

    getParamsTable: function() {
        ///	<summary>        
        /// returns a hashtable with <paramter name, parameter value> pairs
        ///	</summary>
        ///	<returns type="Object" />     
        var paramsTable = new Object();
        for (var i = 0; i < this.getParameters().length; i++) {
            var parameter = this.getParameters()[i];
            paramsTable[parameter.getName()] = parameter.getValue();
        }
        return paramsTable;
    },

    invokeMethod: function(hashkey) {
        this.ensureNoPreviousRequest();
        this.webRequest = Sys.Net.WebServiceProxy.invoke(
            this.getServicePath(),
            this.getServiceMethod(),
            false,
            this.getParamsTable(),
            Function.createDelegate(this, this.onMethodComplete),
            Function.createDelegate(this, this.onMethodFailed),
            hashkey
        );
    },

    ensureNoPreviousRequest: function() {
        if (this.webRequest != null) {
            //this.webRequest.get_executor().abort();            
            this.webRequest = null;
        }
    }
}

BM.Addition['ServiceConstraint'] = BM.ServiceConstraint;
BM.ServiceConstraint.registerClass("BM.ServiceConstraint", BM.Constraint);

// ServiceParameter object

BM.ServiceParameter = function(constraint, name, path) {
    /// <field name="constraint" type="BM.ServiceConstraint">
    ///     The constraint in which the parameter is
    /// </field>    
    BM.ServiceParameter.initializeBase(this);
    this.constraint = constraint;
    this.name = name;
    this.path = path;
    this.getItem().addChanged(Function.createDelegate(this, this.onItemChanged));
}
BM.ServiceParameter.prototype = {
    getPath: function() {
        ///	<summary>        
        ///		returns the path selector from the constraint to the parameter item
        ///	</summary>
        ///	<returns type="String" />      
        return this.path;
    },

    getName: function() {
        ///	<summary>        
        ///		returns the name of the parameter, that will be sent to the script service
        ///	</summary>
        ///	<returns type="String" /> 
        return this.name;
    },

    getValue: function() {
        ///	<summary>        
        ///		returns the bound item's value
        ///	</summary>
        ///	<returns type="String" />      
        var item = this.getItem();
        if (item == null) return '';
        return item.getValue();
    },

    getItem: function() {
        ///	<summary>        
        ///		returns the item bound to the parameter
        ///	</summary>
        ///	<returns type="MItem" />            
        return BM.ItemsNavigator.selectSingle(this.constraint.getItem(), this.getPath());
    },

    onItemChanged: function() {
        ///	<summary>        
        ///		invoked when the corresponding item changes its value and the service needs to be rerun
        ///	</summary>
        this.constraint.item.validate();
    }
}

BM.ServiceParameter.registerClass("BM.ServiceParameter", Sys.Component);

BM.ServiceParameter.getParameters = function(constraint) {
    var parameterNodes = $(constraint.additionNode).children('s');
    var parameters = new Array();
    for (var i = 0; i < parameterNodes.length; i++) {
        parameters[i] = BM.ServiceParameter.create(constraint, parameterNodes[i]);
    }
    return parameters;
}

BM.ServiceParameter.create = function(constraint, parameterNode) {
    return new BM.ServiceParameter(constraint, parameterNode.getAttribute('name'), parameterNode.getAttribute('path'));
}

BM.ServiceParameter.getHashKey = function(parameters) {
    var hashKey = new Sys.StringBuilder();
    for (var i = 0; i < parameters.length; i++) {
        hashKey.append(parameters[i].getValue())
        hashKey.append('#');
    }
    return hashKey.toString();
}